using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using SpaceSlideShow;


namespace Services
{
  public class Background : DrawableGameComponent, SpaceSlideShow.IBackground
  {
    public Background(Game game)
      : base(game)
    {
      game.Services.AddService(typeof(IBackground), this);
      rotate_now = false;
    }

    /// Alla chiusura facciamo pulizia dei servizi istanziati
    protected override void Dispose(bool disposing)
    {
      Game.Services.RemoveService(typeof(IBackground));
      base.Dispose(disposing);
    }

    static readonly float MAX_ROTATION_TIME = 1.0f;
    float rotation_timer = 0.0f;
    public void StartRotation()
    {
      rotate_now = true;
      rotation_timer = MAX_ROTATION_TIME;
    }

    Matrix Rotation(CubeMapFace cube_face)
    {
      switch (cube_face)
      {
        case CubeMapFace.NegativeX:
          return Matrix.CreateRotationY(MathHelper.PiOver2);
        case CubeMapFace.NegativeY:
          return Matrix.CreateRotationZ(MathHelper.Pi) * Matrix.CreateRotationX(MathHelper.PiOver2);
        case CubeMapFace.PositiveZ:
          return Matrix.CreateRotationY(MathHelper.Pi);
        case CubeMapFace.PositiveX:
          return Matrix.CreateRotationY(-MathHelper.PiOver2);
        case CubeMapFace.PositiveY:
          return Matrix.CreateRotationZ(MathHelper.Pi) * Matrix.CreateRotationX(-MathHelper.PiOver2);
        case CubeMapFace.NegativeZ:
          return Matrix.Identity;
        default:
          return Matrix.Identity;
      }
    }

    BasicEffect cube_effect;
    List<VertexBuffer> cube_vertices;
    IndexBuffer cube_indices;
    List<Texture2D> cube_textures;
    VertexBuffer space_dust;
    protected override void LoadContent()
    {
      cube_indices = new IndexBuffer(GraphicsDevice, IndexElementSize.SixteenBits, 6, BufferUsage.WriteOnly);
      cube_indices.SetData(new short[] { 0, 2, 1, 1, 2, 3 });

      cube_vertices = new List<VertexBuffer>(8);
      cube_textures = new List<Texture2D>(8);

      var cube_faces = new[]{
        CubeMapFace.PositiveX,
        CubeMapFace.NegativeX,
        CubeMapFace.PositiveY,
        CubeMapFace.NegativeY,
        CubeMapFace.PositiveZ,
        CubeMapFace.NegativeZ
      };

      foreach (var cube_face in cube_faces)
      {
        cube_textures.Add(Game.Content.Load<Texture2D>(@"Backgrounds\atlantis\" + cube_face.ToString()));
        var vertices = new[]{
          new VertexPositionColorTexture(Vector3.Left  + Vector3.Up   + Vector3.Backward, Color.White, new Vector2(0.0f, 0.0f)),
          new VertexPositionColorTexture(Vector3.Right + Vector3.Up   + Vector3.Backward, Color.White, new Vector2(1.0f, 0.0f)),
          new VertexPositionColorTexture(Vector3.Left  + Vector3.Down + Vector3.Backward, Color.White, new Vector2(0.0f, 1.0f)),
          new VertexPositionColorTexture(Vector3.Right + Vector3.Down + Vector3.Backward, Color.White, new Vector2(1.0f, 1.0f)),
        };
        for (int i = 0; i < vertices.Length; i++)
        {
          vertices[i].Position = Vector3.Transform(vertices[i].Position, Rotation(cube_face));
        }
        var vertex_buffer = new VertexBuffer(GraphicsDevice, typeof(VertexPositionColorTexture), 4, BufferUsage.WriteOnly);
        vertex_buffer.SetData(vertices);
        cube_vertices.Add(vertex_buffer);
      }

      cube_effect = new BasicEffect(GraphicsDevice);

      var r = new Random();
      var space_dust_vertices =
        (from x in Enumerable.Range(-5, 10)
         from y in Enumerable.Range(-2, 2)
         from z in Enumerable.Range(-5, 10)
         let p = new VertexPositionColorTexture(
             new Vector3((float)r.NextDouble() * 0.5f, (float)r.NextDouble() * 0.5f, (float)r.NextDouble() * 0.5f) * 7.5f +
             new Vector3(x, y, z) * 15.0f, Color.White, Vector2.Zero)
         from v in new[] { p, new VertexPositionColorTexture(p.Position + Vector3.Up * 0.1f, p.Color, Vector2.Zero) }
         select v).ToArray();
      space_dust = new VertexBuffer(GraphicsDevice, typeof(VertexPositionColorTexture), space_dust_vertices.Length, BufferUsage.WriteOnly);
      space_dust.SetData(space_dust_vertices);

      View = Matrix.CreateLookAt(Vector3.Zero, Vector3.Forward, Vector3.Up);
      Projection = Matrix.CreatePerspectiveFieldOfView(1.5f, //2.5f * GraphicsDevice.Viewport.Width / 800.0f,
                        GraphicsDevice.Viewport.Width / (float)GraphicsDevice.Viewport.Height, 0.1f, 100.0f);

      base.LoadContent();
    }

    public Matrix CurrentRotation { get; private set; }
    public Matrix View { get; private set; }
    public Matrix Projection { get; private set; }

    public override void Draw(GameTime gameTime)
    {
      GraphicsDevice.BlendState = BlendState.Opaque;

      CurrentRotation = Matrix.CreateFromYawPitchRoll(-0.5f + (float)gameTime.TotalGameTime.TotalMinutes, -1.0f, 0.0f);
      var T = (float)gameTime.TotalGameTime.TotalMinutes;
      if (rotate_now)
      {
        var alpha = MathHelper.SmoothStep(0.0f, 1.0f, MathHelper.SmoothStep(0.0f, 1.0f, Math.Abs(rotation_timer / MAX_ROTATION_TIME)));
        if (rotation_timer < 0.0f)
          alpha = 1.0f - alpha;
        System.Diagnostics.Debug.WriteLine(alpha);
        cube_effect.World =
          CurrentRotation *
          Matrix.CreateRotationY(-MathHelper.TwoPi * alpha);
        if (rotation_timer > 0.0f)
        {
          rotation_timer -= (float)gameTime.ElapsedGameTime.TotalSeconds;
          if (rotation_timer <= 0.0f)
            rotate_now = false;
        }
        if (rotation_timer < 0.0f)
        {
          rotation_timer += (float)gameTime.ElapsedGameTime.TotalSeconds;
          if (rotation_timer >= 0.0f)
            rotate_now = false;
        }
      }
      else
      {
        cube_effect.World = CurrentRotation;
      }
      cube_effect.View = View;
      cube_effect.Projection = Projection;
      cube_effect.TextureEnabled = true;
      for (int i = 0; i < cube_vertices.Count; i++)
      {
        cube_effect.Texture = cube_textures[i];
        GraphicsDevice.SetVertexBuffer(cube_vertices[i]);
        GraphicsDevice.Indices = cube_indices;
        cube_effect.CurrentTechnique.Passes[0].Apply();
        GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 4, 0, 2);
      }

      //var eye_x = 15.0f * (int)(EyePosition.X / 15.0f);
      //var eye_z = 15.0f * (int)(EyePosition.Z / 15.0f);
      //cube_effect.World = Matrix.CreateTranslation(new Vector3(eye_x, 0.0f, eye_z));
      //cube_effect.TextureEnabled = false;
      //cube_effect.VertexColorEnabled = true;
      //GraphicsDevice.SetVertexBuffer(space_dust);
      //cube_effect.CurrentTechnique.Passes[0].Apply();
      //GraphicsDevice.DrawPrimitives(PrimitiveType.LineList, 0, space_dust.VertexCount / 2);

      base.Draw(gameTime);
    }

    //VertexBuffer sv;
    //IndexBuffer si;
    //VertexDeclaration sd;
    //ContentManager content;
    //TextureCube background_texture;
    //Effect sky_effect;
    //EffectParameter world_par, view_par, projection_par, texture_par;
    //protected override void LoadContent()
    //{
    //  content = Game.Content;

    //  sv = new VertexBuffer(GraphicsDevice, VertexPositionColorTexture.SizeInBytes * 100, BufferUsage.WriteOnly);
    //  si = new IndexBuffer(GraphicsDevice, sizeof(UInt16) * 9 * 9 * 6, BufferUsage.WriteOnly, IndexElementSize.SixteenBits);
    //  sd = new VertexDeclaration(GraphicsDevice, VertexPositionColorTexture.VertexElements);
    //  sv.SetData(Splitter.sphere_vertices(10, 10).ToArray());
    //  si.SetData(Splitter.sphere_indices(10, 10).ToArray());

    //  background_texture = content.Load<TextureCube>(@"Backgrounds\nebula");
    //  sky_effect = content.Load<Effect>(@"Backgrounds\Sky");

    //  texture_par = sky_effect.Parameters["Texture"];
    //  world_par = sky_effect.Parameters["World"];
    //  view_par = sky_effect.Parameters["View"];
    //  projection_par = sky_effect.Parameters["Projection"];

    //  base.LoadContent();
    //}

    //public override void Draw(GameTime gameTime)
    //{
    //  Draw(new BoundingSphere(EyePosition, 1.0f));

    //  base.Draw(gameTime);
    //}

    //void Draw(BoundingSphere volume)
    //{
    //  var world = Matrix.CreateTranslation(volume.Center);
    //  texture_par.SetValue(background_texture);
    //  world_par.SetValue(world);
    //  view_par.SetValue(view);
    //  projection_par.SetValue(projection);
    //  sky_effect.Begin(SaveStateMode.SaveState);
    //  sky_effect.CurrentTechnique.Passes[0].Begin();
    //  GraphicsDevice.VertexDeclaration = sd;
    //  GraphicsDevice.Vertices[0].SetSource(sv, 0, VertexPositionColorTexture.SizeInBytes);
    //  GraphicsDevice.Indices = si;
    //  GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 100, 0, 9 * 9 * 2);
    //  sky_effect.CurrentTechnique.Passes[0].End();
    //  sky_effect.End();
    //}

    public bool rotate_now { get; set; }


    public bool IsRotating
    {
      get
      {
        return rotate_now;
      }
    }


    public void StartRotationInverted()
    {
      rotate_now = true;
      rotation_timer = -MAX_ROTATION_TIME;
    }
  }
}